﻿using DM_Sqlite.MappingClass;
using DM_TOOLS.Tools;
using DM_TOOLS.Tools.DBC.DBCCSV;
using DM_TOOLS.Tools.DBC.DBCProcess;
using DM_TOOLS.Tools.SMTP;
using DMSystem_Sqlite_WebAPI.Utility;
using DMSystem_Sqlite_WebAPI.Utility.Feishu;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Quartz;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DMSystem_Sqlite_WebAPI.Utility.Quartz
{
    /// <summary>
    /// 用于监控gz.csv报文的任务（直接解析出需要的数据给前端呈现，并在后台进行合成csv的操作）
    /// </summary>
    public class MyJobforDBC : IJob
    {
        private static readonly string canname = "can";
        private readonly ILogger<MyJobforDBC> _logger;
        private readonly IMemoryCache _memoryCache;//内存缓存
        private readonly IHubContext<SiginalrHub> _hubContext;//signalr
        //private readonly Dictionary<string, List<string>> _aliasMap;//配置别名映射 → 可以维护所有标准信号与别名
        public MyJobforDBC(ILogger<MyJobforDBC> logger, IMemoryCache memoryCache, IHubContext<SiginalrHub> hubContext)
        {
            _logger = logger;
            _memoryCache = memoryCache;
            _hubContext = hubContext;

        }
        public async Task Execute(IJobExecutionContext context)
        {
            var map = context.JobDetail.JobDataMap;
            string? path = map.GetString("gzcsvdata");
            try
            {
                Console.WriteLine($"{path} in {Thread.CurrentThread.ManagedThreadId} run at {DateTime.Now}");
                var userprop = _memoryCache.Get<dm_dbcprops_user>(path);
                var adminprop = _memoryCache.Get<dm_props_admin>("datamonitoradminpara");
                //获得当天日期文件夹路径
                string watchpath = Path.Combine(path, DateTime.Now.ToString("yyyy-MM-dd"));
                string realwatchpath = Path.Combine(watchpath, canname);
                //获得当天日期文件夹保存的路径
                string savedir = Path.Combine(userprop.csvpath, DateTime.Now.ToString("MM_dd"));
                // 判断文件夹是否存在
                if (!Directory.Exists(savedir))
                {
                    // 不存在则创建
                    Directory.CreateDirectory(savedir);
                }
                //获得最新的csv文件
                var file = FileOperator.GetExtensionFiles(realwatchpath, "csv.gz").LastOrDefault();
                //获得缓存里的文件名
                var lastfilename = _memoryCache.Get<string>(path + "dbcfilename");
                //记录dbc文件解析数据
                var mes = _memoryCache.Get<Dictionary<(uint, uint), DbcParserLib.Model.Message[]>>(path + "dbc");
                //获得用于展示的通道列表缓存
                var chanlst = _memoryCache.Get<List<string>>(path + "dbcechartchan");
                //比较最新产生的文件名是否跟缓存里的一样，一样说明没有新文件产生
                if (file != null)
                {
                    if (file.Name != lastfilename)
                    {
                        if (userprop != null && adminprop != null)
                        {
                            //最新文件名存入缓存中
                            await Console.Out.WriteLineAsync($"监控到新文件{file.Name}");
                            _memoryCache.Set<string>(path + "dbcfilename", file.Name);
                            var csvname = await file.Name.ConvertCanFileNameToFAsync();
                            string savepath = Path.Combine(savedir, csvname);
                            //获得此报文文件的解析，然后筛选展示通道并按时间升序和降采样
                            var r1 = await DBCProcess.ExtractCanSignalsAsync(mes, file.FullName);
                            var r2 = await DBCProcess.FilterMultipleSignalSeriesAsync(r1, chanlst);
                            var r3 = await DBCProcess.SortSignalSeriesByTimeAsync(r2);
                            var r4 = await DBCProcess.DownsampleSignalSeriesAsync(r3);
                            // 将 r4 转为只包含 Value 不包括 Time 的结构
                            var r4ValuesOnly = r4.ToDictionary(
                                kv => kv.Key,
                                kv => kv.Value.Select(v => v.Value).ToList()
                            );
                            var spddis = await DBCProcess.CalculateSpeedStatsAsync(r3);
                            //另开启一个线程去解析并保存为csv文件
                            _ = Task.Run(async () =>
                            {
                                await DBCProcess.ResampleAndSaveCsv(r1, savepath);
                            });
                            //获得缓存里的日期，就是监控开始的日期
                            var datetime = _memoryCache.Get<DateTime>(userprop.watchpath + "dbcdatetime");
                            //获得当前日期
                            var nowtime = DateTime.Now;
                            //获得他们之间的日期差，并把这些中间的日期的缓存清除掉
                            var datespan = DateTimeOperator.GetDateStringsBetweenDates(datetime, nowtime);
                            for (int i = 0; i < datespan.Length; i++)
                            {
                                _memoryCache.Remove(userprop.vehicle + datespan[i] + "dbcsumdistance");
                                _memoryCache.Remove(userprop.vehicle + datespan[i] + "dbcfilecount");
                                _memoryCache.Remove(userprop.vehicle + datespan[i] + "dbcreadfilecount");
                                _memoryCache.Remove(userprop.vehicle + datespan[i] + "dbcchanerrorcount");
                                _memoryCache.Remove(userprop.vehicle + datespan[i] + "dbcreadgpsfilecount");
                                _memoryCache.Remove(userprop.vehicle + datespan[i] + "dbcgpserrorcount");
                            }

                            //获得缓存里前一次的累计里程，这里需要用到GetOrCreate，因为过了一天日期就不同了，需要重新创建累计里程缓存
                            var lastsumdis = _memoryCache.GetOrCreate<double>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcsumdistance", value =>
                            {
                                return 0;
                            });
                            //标识监控到的数据的总数
                            var filecount = _memoryCache.GetOrCreate<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcfilecount", value =>
                            {
                                return 0;
                            });
                            //标识一旦监控到-1的异常，则启动此跟踪值并置1，如没有异常则如filecount正常自增
                            var readfilecount = _memoryCache.GetOrCreate<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcreadfilecount", value =>
                            {
                                return 0;
                            });
                            //标识一旦监控到错误gps的异常，则启动此跟踪值并置1，如没有异常则如filecount正常自增
                            var readgpsfilecount = _memoryCache.GetOrCreate<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcreadgpsfilecount", value =>
                            {
                                return 0;
                            });
                            //设置缓存里这一次的累计里程和文件数
                            _memoryCache.Set<double>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcsumdistance", lastsumdis + spddis.DistanceKm);
                            _memoryCache.Set<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcfilecount", filecount + 1);
                            _memoryCache.Set<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcreadfilecount", readfilecount + 1);
                            _memoryCache.Set<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcreadgpsfilecount", readgpsfilecount + 1);
                            var sdsent = _memoryCache.Get<double>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcsumdistance");
                            var countsent = _memoryCache.Get<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcfilecount");
                            var countread = _memoryCache.Get<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcreadfilecount");
                            var gpscountread = _memoryCache.Get<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcreadgpsfilecount");
                            var mailconfig = _memoryCache.Get<SMTPConfig>(userprop.vehicle + "dbcmailconfig");
                            foreach (var i in SiginalrHub.user)
                            {
                                await _hubContext.Clients.Group(i).SendAsync("gzcsv", r4ValuesOnly, file.Name, spddis.AvgSpeed, sdsent, countsent, userprop.watchpath);
                            }
                            //判断是否有-1的值
                            bool hasMinusOne = r4.Values.Any(list => list.Any(item => item.Value == -1));
                            if (hasMinusOne)
                            {
                                //标识-1出现的文件数量
                                var errorcount = _memoryCache.GetOrCreate<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcchanerrorcount", value =>
                                {
                                    return 0;
                                });
                                _memoryCache.Set(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcchanerrorcount", errorcount + 1);
                                //获得实际的-1文件数量
                                var errorcountfile = _memoryCache.Get<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcchanerrorcount");
                                //第一次出现-1时就把readfilecount置1，后面再出现-1就不会置1了
                                if (errorcountfile == 1)
                                {
                                    //一旦监控到-1的异常，则启动此跟踪值并置1，与errorcountfile一致
                                    _memoryCache.Set<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcreadfilecount", 1);
                                }
                                //当-1文件数达到120个，说明至少有10分钟了，但不确定是否连续-1
                                if (errorcountfile > 120)
                                {
                                    //说明-1错误一直持续了120个
                                    if (errorcountfile == countread)
                                    {
                                        //await FeishuRobot.SendFeishuMessage("车号：" + userprop.vehicle + "-" + userprop.ip + "(" + userprop.remarks + ")：启动有通道连续10分钟出现-1。最后文件名为：" + file.Name);
                                        await FeishuRobot.SendFeishuMessage($"车号：{userprop.vehicle}-{userprop.ip}-{userprop.remarks}: 有通道连续10分钟出现-1。最后文件名为：{file.Name}");
                                        _memoryCache.Set<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcreadfilecount", 0);
                                        _memoryCache.Set<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcchanerrorcount", 0);
                                    }
                                    //说明-1的数量小于监控到的数量，表明出现-1不连续，则不会发飞书
                                    else
                                    {
                                        _memoryCache.Set<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcreadfilecount", 0);
                                        _memoryCache.Set<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcchanerrorcount", 0);
                                    }
                                }
                            }
                            //处理经纬度的异常
                            if (r4["lat"].Any(v => v.Value == 0) || r4["lon"].Any(v => v.Value == 0))
                            {
                                _logger.LogWarning("车号：" + userprop.vehicle + "-" + userprop.ip + "(" + userprop.remarks + ")：经纬度为0或通道名没有匹配上。最后文件名为：" + file.Name);
                                //标识GPS出现error的文件数量
                                var gpserrorcount = _memoryCache.GetOrCreate<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcgpserrorcount", value =>
                                {
                                    return 0;
                                });
                                _memoryCache.Set(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcgpserrorcount", gpserrorcount + 1);
                                //获得实际的错误gps文件数量
                                var gpserrorcountfile = _memoryCache.Get<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcgpserrorcount");

                                if (gpserrorcountfile == 1)
                                {
                                    //一旦监控到错误gps的异常，则启动此跟踪值并置1，与errorcountfile一致
                                    _memoryCache.Set<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcreadgpsfilecount", 1);
                                }
                                //当-1文件数达到120个，说明至少有10分钟了，但不确定是否连续-1
                                if (gpserrorcountfile > 120)
                                {
                                    //说明gps错误一直持续了120个
                                    if (gpserrorcountfile == gpscountread)
                                    {
                                        //await FeishuRobot.SendFeishuMessage("车号：" + userprop.vehicle + "-" + userprop.ip + "(" + userprop.remarks + ")：经纬度为0或通道名没有匹配上持续10分钟。最后文件名为：" + file.Name);
                                        await FeishuRobot.SendFeishuMessage($"车号：{userprop.vehicle}-{userprop.ip}-{userprop.remarks}: 经纬度为0或通道名没有匹配上持续10分钟。最后文件名为：{file.Name}");
                                        _memoryCache.Set<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcreadgpsfilecount", 0);
                                        _memoryCache.Set<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcgpserrorcount", 0);
                                    }
                                    //说明gps错误的数量小于监控到的数量，表明出现gps错误不连续
                                    else
                                    {
                                        _memoryCache.Set<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcreadgpsfilecount", 0);
                                        _memoryCache.Set<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "dbcgpserrorcount", 0);
                                    }
                                }
                            }

                            var res = SMTPWarnContent.CreateWarnContent(r4, userprop.vehicle, adminprop.accerrorvalue, adminprop.diserrorvalue, userprop.frontdis, userprop.reardis);
                            //先判断有没有异常，没有就不用费劲去看字典了
                            if (res != "")
                            {
                                //先获得mailconfig缓存
                                if (mailconfig != null)
                                {
                                    //再从缓存里看有没有mailerror，如果没有说明第一次碰到error
                                    var firstmailerror = _memoryCache.Get<string>(userprop.vehicle + "dbcmailerror");
                                    if (firstmailerror == null)
                                    {
                                        //第一次碰到错误发送邮件
                                        //await SMTPMail.SendEmailAsync(mailconfig, res);
                                        await FeishuRobot.SendFeishuMessage("车号：" + userprop.vehicle + "-" + userprop.ip + "(" + userprop.remarks + ")" + "通道可能损坏：" + res + "。文件名为：" + file.Name);
                                        //如果第一次碰到，则创建缓存并设置为第一次的错误信息
                                        var mailerror = _memoryCache.GetOrCreate<string>(userprop.vehicle + "dbcmailerror", value =>
                                        {
                                            return res;
                                        });
                                        //再创建缓存记录第一次发送的时间
                                        var starttime = _memoryCache.GetOrCreate<DateTime>(userprop.vehicle + "dbcsendtime", value =>
                                        {
                                            return DateTime.Now;
                                        });
                                    }
                                    //说明缓存里已经有mailerror了，不是第一次出现问题了
                                    else
                                    {
                                        //判断当前的与上一次的是否问题一致，不一致再发邮件，再覆盖记录发送时间
                                        if (res != firstmailerror)
                                        {
                                            _memoryCache.Set<string>(userprop.vehicle + "dbcmailerror", res);
                                            //await SMTPMail.SendEmailAsync(mailconfig, res);
                                            await FeishuRobot.SendFeishuMessage("车号：" + userprop.vehicle + "-" + userprop.ip + "(" + userprop.remarks + ")" + "通道可能损坏：" + res + "。文件名为：" + file.Name);

                                            _memoryCache.Set<DateTime>(userprop.vehicle + "dbcsendtime", DateTime.Now);
                                        }
                                        //如果一致，则判断是否经过了1小时，然后再发送邮件提醒
                                        else
                                        {
                                            //先获取上一次缓存里的发送时间
                                            var lastsendtime = _memoryCache.Get<DateTime>(userprop.vehicle + "dbcsendtime");
                                            if (SMTPWarnContent.IsPastOneHour(lastsendtime, mailconfig.emailtimespan))
                                            {
                                                //await SMTPMail.SendEmailAsync(mailconfig, res);
                                                await FeishuRobot.SendFeishuMessage("车号：" + userprop.vehicle + "-" + userprop.ip + "(" + userprop.remarks + ")" + "通道可能损坏：" + res + "。文件名为：" + file.Name);
                                                _memoryCache.Set<DateTime>(userprop.vehicle + "dbcsendtime", DateTime.Now);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message);
                foreach (var i in SiginalrHub.user)
                {
                    await _hubContext.Clients.Group(i).SendAsync("gzcsvexception", path, ex.Message);
                }
            }
           



        }
    }
}
